using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using Clandestine.Base;

namespace Clandestine.Graphics
{
	internal static class BitmapHelper
	{
		/// <summary>
		/// Decides whether or not the given bitmap needs chroma-keying, and returns the resulting Bitmap.
		/// </summary>
		/// <param name="bmp">
		/// A <see cref="Bitmap"/> that may or may not need chroma-keying.
		/// </param>
		/// <param name="treatAsIndexed">
		/// A <see cref="System.Boolean"/> to specify whether or not to chroma-key with (255,0,126).
		/// </param>
		/// <returns>
		/// The <see cref="Bitmap"/> resultant bitmap.
		/// </returns>
        public static Bitmap ChromaKeyIfNecessary(Bitmap bmp, bool treatAsIndexed)
        {
            if (treatAsIndexed || (bmp.PixelFormat & PixelFormat.Indexed) == PixelFormat.Indexed)
            {
                Log.i("Got an indexed bitmap. Doing chroma-keying with zelda-pink chroma. (225,0,126)");
                Bitmap bmpNew = ConvertTo32bppBitmap(bmp);
                bmp.Dispose();
                bmp = bmpNew;
                ChromaKey32bppBitmap(bmp, Color.FromArgb(225, 0, 126));
                return bmp;
            }

            // If 24bpp, chroma-key based on pixel at coords (0,0) (top-left)
            if (!((bmp.PixelFormat & PixelFormat.Alpha) == PixelFormat.Alpha))
            {
                DateTime dtConvStart = DateTime.Now;

                // ah. chroma-keying time! what fun.
                Bitmap bmpNew = ConvertTo32bppBitmap(bmp);
                bmp.Dispose();

                bmp = bmpNew;

                ChromaKey32bppBitmap(bmp, bmp.GetPixel(0, 0));

                DateTime dtConvEnd = DateTime.Now;
                Log.i("Bitmap chroma-keying of bitmap of size {0} took {1} ms", bmp.Size, (dtConvEnd - dtConvStart).TotalMilliseconds);
            }

            return bmp;
        }

		/// <summary>
		/// Converts a given bitmap to 32bpp.
		/// </summary>
		/// <param name="bmp">
		/// A <see cref="Bitmap"/> to be converted.
		/// </param>
		/// <returns>
		/// The converted <see cref="Bitmap"/>.
		/// </returns>
        public static Bitmap ConvertTo32bppBitmap(Bitmap bmp)
        {
            Bitmap newBmp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format32bppArgb);

            BitmapData bdRead = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size),
                        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
            BitmapData bdWrite = newBmp.LockBits(new Rectangle(Point.Empty, bmp.Size),
                ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

            // Temporary buffer ugh
            byte[] xbuf = new byte[bdRead.Stride * bdRead.Height];
            Marshal.Copy(bdRead.Scan0, xbuf, 0, bdRead.Stride * bdRead.Height);
            Marshal.Copy(xbuf, 0, bdWrite.Scan0, bdRead.Stride * bdRead.Height);

            bmp.UnlockBits(bdRead);
            newBmp.UnlockBits(bdWrite);

            return newBmp;
        }

		/// <summary>
		/// Chroma-keys the given 32bpp bitmap.
		/// </summary>
		/// <param name="bmp">
		/// A <see cref="Bitmap"/> to be chroma-keyed.
		/// </param>
		/// <param name="chroma">
		/// The colour to use for the chroma-keying <see cref="Color"/>.
		/// </param>
        public static void ChromaKey32bppBitmap(Bitmap bmp, Color chroma)
        {
            BitmapData bd = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size),
                        ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

            unsafe
            {
                int len = bd.Stride * bd.Height;

                byte* ptr = (byte*)bd.Scan0.ToPointer();

                byte* ptrStop = ptr + len;

                while (ptr < ptrStop)
                {
                    if (*ptr == chroma.B && *(ptr + 1) == chroma.G
                        && *(ptr + 2) == chroma.R)
                        *(ptr + 3) = 0; // ...then set the alpha to 0

                    ptr += 4;
                }
            }

            bmp.UnlockBits(bd);
        }

		public static void SetRGBToWhitePreserveAlpha(Bitmap bmp)
		{
			BitmapData bd = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size),
			                             ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
			
			unsafe
			{
				byte* ptr = (byte*)(bd.Scan0.ToPointer());
				byte* ptrEnd = (byte*)(bd.Scan0.ToPointer()) + bd.Stride * bd.Height;
				int count = 1;
				
				while (ptr < ptrEnd)
				{
					// Remember, BGRA order!
					if (count % 4 != 0)
						*ptr = 255;
					ptr++;
					count++;
				}
			}
			
			bmp.UnlockBits(bd);
		}

	}
}

